using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Tile : Interactive
{
	public enum State
	{
		Regular,
		Walk,
		Attack,
		PickUp,
		Action,
	}

	[Header("Tile")]
	public Interactive objectOnTop;

	public Tile left;		// This is only used in the editor
	public Tile up;			// This is only used in the editor
	public Tile right;		// This is only used in the editor
	public Tile down;		// This is only used in the editor
	public List<Tile> neighbours = new List<Tile>();	// These are the neighbours used ingame
	public List<Door> neighbourDoors = new List<Door>();

	private State state;
	private SpriteRenderer sprite;

	protected override void Awake()
	{
		base.Awake();

		sprite = GetComponent<SpriteRenderer>();

		neighbours.Clear(); // If there was any neighbours added in the inspector
		if (left) neighbours.Add(left);
		if (up) neighbours.Add(up);
		if (right) neighbours.Add(right);
		if (down) neighbours.Add(down);

		left = up = right = down = null;
		
		// Removes tiles hidden by scenery - Preprocess after all tiles are linked to their neighbours
		RemoveConnections();
	}
	
	// Called in Awake by Interactive
	public override void RecalculateAreas()
	{
		currentTile = null;
		selectArea.Add(this);
	}

	public void SetObjectOnTop(Interactive _inter)
	{
		objectOnTop = _inter;
	}

	public bool IsNeighbour(Tile _other)
	{
		return neighbours.Contains(_other);
	}
	
	public bool IsConnectionOpen(Tile _other)
	{
		foreach (Door door in neighbourDoors)
		{
			if (door.IsConnected(this, _other) && !door.IsOpen())
			{
				return false;
			}
		}
		
		return true;
	}

	public void SetState(State _state)
	{
		state = _state;
		switch (_state)
		{
		case State.Regular:
			sprite.sprite = RefManager.Instance.tileRegular;
			break;
			
		case State.Walk:
			sprite.sprite = RefManager.Instance.tileWalk;
			break;
			
		case State.Attack:
			sprite.sprite = RefManager.Instance.tileAttack;
			break;

		case State.PickUp:
			sprite.sprite = RefManager.Instance.tilePickUp;
			break;
			
		case State.Action:
			sprite.sprite = RefManager.Instance.tileAction;
			break;
		}
	}
	
	protected override void FingerClick()
	{
		if (state == State.Walk)
		{
			InitiativeManager.Instance.current.GotoTile(this);
		}
	}

	#region Neighbours & Editor
	public void FindAllNeighbours()
	{
		/*List<Tile> hits = new List<Tile>();
		hits.Add(FloorManager.Instance.GetTile(transform.position + Vector3.up));
		hits.Add(FloorManager.Instance.GetTile(transform.position + Vector3.down));
		hits.Add(FloorManager.Instance.GetTile(transform.position + Vector3.right));
		hits.Add(FloorManager.Instance.GetTile(transform.position + Vector3.left));
		
		foreach (Tile hit in hits)
		{
			if (hit != null && !neighbours.Contains(hit))
			{
				neighbours.Add(hit);
			}
		}*/
		
		left = CastForTile(transform.position + Vector3.left);
		up = CastForTile(transform.position + Vector3.up);
		right = CastForTile(transform.position + Vector3.right);
		down = CastForTile(transform.position + Vector3.down);
	}

	public void RemoveConnections()
	{
		for (int i = neighbours.Count - 1; i >= 0; i--)
		{
			if (GetScenery(neighbours[i].transform.position) != null)
			{
				neighbours.RemoveAt(i);
			}
		}
	}

	public static Tile CastForTile(Vector3 _position)
	{
		Collider2D col = Physics2D.OverlapPoint(_position, 1 << LayerMask.NameToLayer("Tile"));
		if (col)
		{
			return col.GetComponent<Tile>();
		}
		else
		{
			return null;
		}
	}
	#endregion

	#region STATIC
	public static float Distance(Tile _tile1, Tile _tile2)
	{
		return Mathf.Abs(_tile1.transform.position.x - _tile2.transform.position.x)
			+ Mathf.Abs(_tile1.transform.position.y - _tile2.transform.position.y);
	}
	
	public static Tile ClosestTile(Tile _tile, List<Tile> _list)
	{
		Tile closest = null;
		float closestValue = float.PositiveInfinity;
		foreach (Tile tile in _list)
		{
			float distance = (tile.transform.position - _tile.transform.position).sqrMagnitude;
			if (distance < closestValue)
			{
				closest = tile;
				closestValue = distance;
			}
		}
		
		return closest;
	}

	public static Interactive ClosestInteractive(Tile _from, List<Interactive> _inters)
	{
		Interactive closest = null;
		float closestValue = float.PositiveInfinity;
		foreach (Interactive inter in _inters)
		{
			float distance = Tile.Distance(_from, inter.currentTile);
			if (distance < closestValue)
			{
				closest = inter;
				closestValue = distance;
			}
		}
		
		return closest;
	}
	#endregion

	#region GIZMOS
	protected override void OnDrawGizmosSelected()
	{
		if (UnityEditor.EditorApplication.isPlaying)
		{
			foreach (Tile tile in neighbours)
			{
				if (this.IsConnectionOpen(tile))
				{
					DrawNeighbourGizmo(transform.position, tile.transform.position, "Connect");
				}
				else
				{
					DrawNeighbourGizmo(transform.position, tile.transform.position, "Door");
				}
			}
		}
		else
		{
			DrawNeighbourGizmo(this, left, "Left");
			DrawNeighbourGizmo(this, up, "Up");
			DrawNeighbourGizmo(this, right, "Right");
			DrawNeighbourGizmo(this, down, "Down");
		}
	}

	protected static void DrawNeighbourGizmo(Vector3 _self, Vector3 _neighbour, string _state)
	{
		float rightDot = Vector3.Dot(_self - _neighbour, Vector3.right);
		float upDot = Vector3.Dot(_self - _neighbour, Vector3.up);
		
		string direction = "";
		if (rightDot == 1 && upDot == 0)
		{
			direction = "Left";
		}
		else if (rightDot == -1 && upDot == 0)
		{
			direction = "Right";
		}
		else if (rightDot == 0 && upDot == -1)
		{
			direction = "Up";
		}
		else if (rightDot == 0 && upDot == 1)
		{
			direction = "Down";
		}
		
		Gizmos.DrawIcon(_self, "Tile/gizmo" + _state + direction);
	}
	
	private static void DrawNeighbourGizmo(Tile _tile, Tile _neighbour, string _direction)
	{
		if (_neighbour)
		{
			bool hasDoor = false;
			foreach (Door door in _tile.neighbourDoors)
			{
				if (door.IsConnected(_tile, _neighbour))
				{
					hasDoor = true;
				}
			}
			
			if (hasDoor)
			{
				Gizmos.DrawIcon(_tile.transform.position, "Tile/gizmoDoor" + _direction);
			}
			else
			{
				Gizmos.DrawIcon(_tile.transform.position, "Tile/gizmoConnect" + _direction);
			}
		}
		else
		{
			Gizmos.DrawIcon(_tile.transform.position, "Tile/gizmoNone" + _direction);
		}
	}
	#endregion
}
